﻿using LevelEditor.Model.Entity;
using LevelEditor.Model.IO;
using LevelEditor.Model.Services;
using LevelEditor.Utils;
using Microsoft.Win32;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
using System.Windows.Threading;

namespace LevelEditor.View.Windows
{
    /// <summary>
    /// Interaction logic for SpriteSheetManagerWindow.xaml
    /// </summary>
    public partial class SpriteSheetManagerWindow : Window
    {
    // Fields

        private OpenFileDialog addSpritesDialog;
        private DispatcherTimer animationTimer;

        private int currentFrame;
        
        private List<Image> sprites;
        private SpriteSheet spriteSheet;
        private List<SpriteSheet> spriteSheets;

        // Methods
        public SpriteSheetManagerWindow()
        {
            this.InitializeComponent();
            base.Loaded += new RoutedEventHandler(this.SpriteSheetManagerWindow_Loaded);
            this.closeBtn.Click += new RoutedEventHandler(this.closeBtn_Click);
            this.createBtn.Click += new RoutedEventHandler(this.createBtn_Click);
            this.removeBtn.Click += new RoutedEventHandler(this.removeBtn_Click);
            this.addSpriteBtn.Click += new RoutedEventHandler(this.addSpriteBtn_Click);
            this.saveBtn.Click += new RoutedEventHandler(this.saveBtn_Click);
            this.frameDuration.KeyUp += new KeyEventHandler(this.frameDuration_KeyUp);

            this.sprites = new List<Image>();
            this.spriteSheets = new List<SpriteSheet>();
            this.animationTimer = new DispatcherTimer();
            this.animationTimer.Interval = new TimeSpan(0x51615L);
            this.animationTimer.Tick += new EventHandler(this.animationTimer_Tick);
            this.spriteSheetList.SelectionChanged += new SelectionChangedEventHandler(this.spriteSheetList_SelectionChanged);

            this.frameWidth.Content = "???";
            this.frameHeight.Content = "???";
            this.name.Text = "???";
            this.frameDuration.Text = "33";
            this.addSpriteBtn.IsEnabled = false;
            this.saveBtn.IsEnabled = false;
        }

        protected void addSpriteBtn_Click(object sender, RoutedEventArgs e)
        {
            if (this.addSpritesDialog == null)
            {
                this.addSpritesDialog = new OpenFileDialog();
                this.addSpritesDialog.AddExtension = true;
                this.addSpritesDialog.Multiselect = true;
                this.addSpritesDialog.Filter = "Image|*.png;*.jpeg;*.jpg";
                this.addSpritesDialog.FileOk += new CancelEventHandler(this.addSpritesDialog_FileOk);
            }
            this.addSpritesDialog.ShowDialog();
        }

        protected void addSpritesDialog_FileOk(object sender, CancelEventArgs e)
        {
            string appPath = FileUtil.AppStartUpPath;
            DirectoryInfo assetsSpritesDir = new DirectoryInfo(appPath + @"Assets\SpritesSheet\" + this.spriteSheet.SpriteSheetID);
            if (!assetsSpritesDir.Exists)
            {
                assetsSpritesDir.Create();
            }
            Stream[] fileStreams = this.addSpritesDialog.OpenFiles();
            string[] fileNames = this.addSpritesDialog.SafeFileNames;
            for (int i = 0; i < fileStreams.Length; i++)
            {
                string filePath = string.Concat(new object[] { appPath, @"Assets\SpritesSheet\", this.spriteSheet.SpriteSheetID, @"\", fileNames[i] });
                FileInfo assetFile = new FileInfo(filePath);
                if (assetFile.Exists)
                {
                    assetFile.Delete();
                }
                using (FileStream fileStream = File.Create(filePath))
                {
                    fileStreams[i].CopyTo(fileStream);
                }
                Image image = this.LoadImageFromPath(filePath);

                spriteSheet.CellWidth = (int)image.Width;
                spriteSheet.CellHeight = (int)image.Height;

                this.frameWidth.Content = spriteSheet.CellWidth.ToString();
                this.frameHeight.Content = spriteSheet.CellHeight.ToString();

                Border border = new Border {
                    BorderThickness = new Thickness(1.0),
                    Child = image
                };
                this.sprites.Add(image);
                this.SpriteGrid.Children.Add(border);
            }
            this.PlayAnimation();
        }

        protected void animationTimer_Tick(object sender, EventArgs e)
        {
            if (this.sprites.Count == 0)
                return;

            this.currentFrame++;
            if (this.currentFrame == this.sprites.Count)
            {
                this.currentFrame = 0;
            }
            this.previewImage.Source = this.sprites[this.currentFrame].Source.Clone();
        }

        protected void closeBtn_Click(object sender, RoutedEventArgs e)
        {
            base.Close();
        }

        protected void createBtn_Click(object sender, RoutedEventArgs e)
        {
            SpriteSheet newSpriteSheet = new SpriteSheet {
                Name = "New SpriteSheet!",
                SpriteSheetID = IDsService.GetNewSpriteSheetID(),
                FrameDuration = 33
            };
            this.spriteSheets.Add(newSpriteSheet);
            this.spriteSheetList.Items.Add(newSpriteSheet.Name);
            this.spriteSheetList.SelectedIndex = spriteSheets.IndexOf(newSpriteSheet);

            this.addSpriteBtn.IsEnabled = true;
            this.saveBtn.IsEnabled = true;
        }

        protected void frameDuration_KeyUp(object sender, KeyEventArgs e)
        {
            int duration = 0;
            if (int.TryParse(this.frameDuration.Text, out duration))
            {
                this.animationTimer.Interval = new TimeSpan(0, 0, 0, 0, duration);
                this.spriteSheet.FrameDuration = duration;
            }
        }

        private void GenerateSpriteSheet()
        {
            int currRow = 0;
            int currCol = 0;
            int cellWidth = spriteSheet.CellWidth;
            int cellHeight = spriteSheet.CellHeight;

            string appPath = FileUtil.AppStartUpPath;
            DirectoryInfo assetsSpritesDir = new DirectoryInfo(appPath + @"Assets\SpritesSheet\" + this.spriteSheet.SpriteSheetID);
            if (assetsSpritesDir.Exists)
            {
                FileInfo[] files = assetsSpritesDir.GetFiles();
                this.spriteSheet.EndCellIndex = files.Length - 1;
                int cols = (int) Math.Ceiling(Math.Sqrt((double) files.Length));
                int rows = cols;
                int ssWidth = cols * cellWidth;
                int ssHeight = rows * cellHeight;
                ssWidth = Math.Max(ssWidth, ssWidth + (ssWidth % 8));
                ssHeight = Math.Max(ssHeight, ssHeight + (ssHeight % 8));
                System.Drawing.Bitmap bmp = new System.Drawing.Bitmap(ssWidth, ssHeight);
                System.Drawing.Graphics g = System.Drawing.Graphics.FromImage(bmp);
                System.Drawing.Rectangle rect = new System.Drawing.Rectangle
                {
                    X = 0,
                    Y = 0,
                    Width = cellWidth,
                    Height = cellHeight
                };
                for (int i = 0; i < files.Length; i++)
                {
                    string filePath = files[i].FullName;
                    MemoryStream ms = new MemoryStream();
                    FileStream stream = new FileStream(filePath, FileMode.Open, FileAccess.Read);
                    ms.SetLength(stream.Length);
                    stream.Read(ms.GetBuffer(), 0, (int) stream.Length);
                    ms.Flush();
                    stream.Close();
                    System.Drawing.Bitmap srcBitmap = (System.Drawing.Bitmap)System.Drawing.Image.FromStream(ms);
                    g.DrawImage(srcBitmap, currCol * cellWidth, currRow * cellHeight, rect, System.Drawing.GraphicsUnit.Pixel);
                    currCol++;
                    if (currCol == cols)
                    {
                        currCol = 0;
                        currRow++;
                    }
                }
                g.Dispose();
                using (MemoryStream ms = new MemoryStream())
                {
                    bmp.Save(ms, System.Drawing.Imaging.ImageFormat.Png);
                    byte[] imageBytes = ms.ToArray();
                    using (FileStream fs = File.Create(string.Concat(new object[] { appPath, @"Assets\SpritesSheet\", this.spriteSheet.SpriteSheetID, ".png" })))
                    {
                        fs.Write(imageBytes, 0, imageBytes.Length);
                        fs.Close();
                    }
                }
                bmp.Dispose();
            }
        }

        private Image LoadImageFromPath(string filePath)
        {
            MemoryStream ms = new MemoryStream();
            FileStream stream = new FileStream(filePath, FileMode.Open, FileAccess.Read);
            ms.SetLength(stream.Length);
            stream.Read(ms.GetBuffer(), 0, (int) stream.Length);
            ms.Flush();
            stream.Close();
            BitmapImage img = new BitmapImage();
            img.BeginInit();
            img.StreamSource = ms;
            img.CacheOption = BitmapCacheOption.OnLoad;
            img.EndInit();
            return new Image { Source = img, Width = img.PixelWidth, Height = img.PixelHeight, MaxHeight = 100.0, MaxWidth = 100.0 };
        }

        private void LoadImagesFromSpriteSheetFolder(int spriteSheetID)
        {
            this.sprites.Clear();

            DirectoryInfo assetsSpritesDir = new DirectoryInfo(FileUtil.AppStartUpPath + @"Assets\SpritesSheet\" + this.spriteSheet.SpriteSheetID);
            if (assetsSpritesDir.Exists)
            {
                FileInfo[] files = assetsSpritesDir.GetFiles();
                for (int i = 0; i < files.Length; i++)
                {
                    string filePath = files[i].FullName;
                    Image image = this.LoadImageFromPath(filePath);

                    spriteSheet.CellWidth = (int)image.Width;
                    spriteSheet.CellHeight = (int)image.Height;

                    this.frameWidth.Content = spriteSheet.CellWidth.ToString();
                    this.frameHeight.Content = spriteSheet.CellHeight.ToString();

                    Border border = new Border {
                        BorderThickness = new Thickness(1.0),
                        Child = image
                    };
                    this.sprites.Add(image);
                    this.SpriteGrid.Children.Add(border);
                }
            }
        }

        private void LoadSpriteSheetByIndex(int index)
        {
            this.SpriteGrid.Children.Clear();
            this.StopAnimation();
            this.previewImage.Source = null;

            this.sprites.Clear();
            this.spriteSheet = this.spriteSheets[index];
            this.name.Text = this.spriteSheet.Name;
            this.frameWidth.Content = this.spriteSheet.CellWidth.ToString();
            this.frameHeight.Content = this.spriteSheet.CellHeight.ToString();
            this.frameDuration.Text = this.spriteSheet.FrameDuration.ToString();
            this.LoadImagesFromSpriteSheetFolder(this.spriteSheet.SpriteSheetID);
            this.PlayAnimation();

            this.addSpriteBtn.IsEnabled = true;
            this.saveBtn.IsEnabled = true;
        }

        private void LoadSpriteSheetsList()
        {
            this.spriteSheets = SpriteSheetIO.LoadSpriteSheets();
            for (int i = 0; i < this.spriteSheets.Count; i++)
            {
                this.spriteSheetList.Items.Add(this.spriteSheets[i].Name);
            }
        }

        private void PlayAnimation()
        {
            this.animationTimer.Start();
        }

        protected void removeBtn_Click(object sender, RoutedEventArgs e)
        {
            if (this.spriteSheetList.SelectedIndex == -1)
                return;

            this.spriteSheetList.Items.RemoveAt(this.spriteSheets.IndexOf(this.spriteSheet));
            this.spriteSheets.Remove(this.spriteSheet);
            this.sprites.Clear();

            DirectoryInfo assetsSpritesDir = new DirectoryInfo(FileUtil.AppStartUpPath + @"Assets\SpritesSheet\" + this.spriteSheet.SpriteSheetID);
            if (assetsSpritesDir.Exists)
            {
                assetsSpritesDir.Delete(true);
            }

            SpriteSheetIO.SaveSpriteSheets(this.spriteSheets);

            this.SpriteGrid.Children.Clear();
            this.StopAnimation();
            this.previewImage.Source = null;

            this.spriteSheetList.SelectedIndex = -1;
            this.frameWidth.Content = "???";
            this.frameHeight.Content = "???";
            this.name.Text = "???";
            this.frameDuration.Text = "33";
            this.addSpriteBtn.IsEnabled = false;
            this.saveBtn.IsEnabled = false;
        }

        protected void saveBtn_Click(object sender, RoutedEventArgs e)
        {
            this.GenerateSpriteSheet();
            this.SaveChanges();
            MessageBox.Show("Sprite Sheet '"+ this.spriteSheet.Name +"' generated and saved!", "Task Completed", MessageBoxButton.OK, MessageBoxImage.Information);
        }

        private void SaveChanges()
        {
            this.spriteSheet.Name = name.Text;
            this.spriteSheet.FrameDuration = int.Parse(this.frameDuration.Text);
            this.spriteSheet.StartCellIndex = 0;

            int curIndex = spriteSheets.IndexOf(spriteSheet);
            this.spriteSheetList.Items[curIndex] = spriteSheet.Name;
            
            SpriteSheetIO.SaveSpriteSheets(this.spriteSheets);

            this.spriteSheetList.SelectedIndex = curIndex;
        }

        protected void spriteSheetList_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            if (spriteSheetList.SelectedIndex == -1)
            {
                this.addSpriteBtn.IsEnabled = false;
                this.saveBtn.IsEnabled = false;
                return;
            }

            this.LoadSpriteSheetByIndex(this.spriteSheetList.SelectedIndex);
        }

        protected void SpriteSheetManagerWindow_Loaded(object sender, RoutedEventArgs e)
        {
            this.LoadSpriteSheetsList();
        }

        private void StopAnimation()
        {
            this.animationTimer.Stop();
        }

    }
}
